package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import com.heima.behavior.dto.ArticleOperationMessage;
import com.heima.behavior.entity.ArticleAggregatorResult;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class HotArticleServiceImpl implements IHotArticleService {
    @Autowired
    private IApArticleService articleService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 查询最近5天发布的文章，计算文章的得分，放到Redis中
     */
    @Override
    public void compute() {
//        查询最近5天发布的文章
        LambdaQueryWrapper<ApArticle> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ApArticle::getIsDelete, false);
        wrapper.eq(ApArticle::getIsDown, false);
        Date fromDate = getFromAndToDate(-30);
        Date toDate = getFromAndToDate(0);
        wrapper.between(ApArticle::getPublishTime, fromDate, toDate);
        List<ApArticle> articleList = articleService.list(wrapper);

//        计算文章的得分
        if (!CollectionUtils.isEmpty(articleList)) {
            for (ApArticle apArticle : articleList) {
                int score = computeScore(apArticle);
                if (score > 0) {
//                   - 将热点文章存入Redis实现排行榜
                    String redisKeyPrefix = "leadnews:article:hot:page:";
//                   - 将所有文章存入到Redis首页排行榜
                    String indexKey = redisKeyPrefix + "0";
//                   - 获取文章JSON数据
                    ApArticle cache = new ApArticle();
                    cache.setId(apArticle.getId());
                    cache.setTitle(apArticle.getTitle());
                    cache.setImages(apArticle.getImages());
                    cache.setAuthorId(apArticle.getAuthorId());
                    cache.setAuthorName(apArticle.getAuthorName());
                    cache.setLayout(apArticle.getLayout());
                    cache.setStaticUrl(apArticle.getStaticUrl());
                    String articleJson = JSON.toJSONString(cache);
                    redisTemplate.opsForZSet().add(indexKey, articleJson, score);


//                   - 将文章存入当前频道排行榜
                    String channelKey = redisKeyPrefix + apArticle.getChannelId();
                    redisTemplate.opsForZSet().add(channelKey, articleJson, score);

//                   - 设置缓存排行榜有效期 为1天
                    redisTemplate.expire(indexKey, 1, TimeUnit.DAYS);
                    redisTemplate.expire(channelKey, 1, TimeUnit.DAYS);
                }
            }
        }

    }


    /**
     * 为入参的文章计算分值
     *
     * @param article
     * @return
     */
    private int computeScore(ApArticle article) {
        int score = 0;
        // 阅读 +1  点赞 +3  评论 +5  收藏 +8
        if (article.getViews() != null) {
            score += article.getViews() * 1;
        }
        if (article.getLikes() != null) {
            score += article.getLikes() * 3;
        }
        if (article.getComment() != null) {
            score += article.getComment() * 5;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * 8;
        }
        return score;
    }

    /**
     * 计算当日的增量分数  当日的操作整体权重*3
     *
     * @param result
     * @return
     */
    private Long computeScore(ArticleAggregatorResult result) {
        Long score = 0L;
        score += result.getView() * 1 * 3;
        score += result.getLike() * 3 * 3;
        score += result.getComment() * 5 * 3;
        score += result.getCollect() * 8 * 3;
        return score;
    }

    /**
     * 获取指定天数日期 正数：未来时间  负数：以前时间
     *
     * @param datecount
     * @return
     */
    public static Date getFromAndToDate(Integer datecount) {
        //创建日期对象
        Calendar calendar = Calendar.getInstance();
        //设置日期
        calendar.add(Calendar.DATE, datecount);
        //设置时分秒
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        return calendar.getTime();
    }

    /**
     * 根据实时计算文章热点分值，更新Redis缓存，更新数据库文章表
     *
     * @param result 聚合文章操作结果
     */
    @Override
    public void updateCacheAndDb(ArticleAggregatorResult result) {
//        计算当日的操作产生的增量得分
        Long plusScore = computeScore(result);
//        查询Redis中文章是否存在
        ApArticle byId = articleService.getById(result.getArticleId());
        ApArticle cache = new ApArticle();
        cache.setId(byId.getId());
        cache.setTitle(byId.getTitle());
        cache.setImages(byId.getImages());
        cache.setAuthorId(byId.getAuthorId());
        cache.setAuthorName(byId.getAuthorName());
        cache.setLayout(byId.getLayout());
        cache.setStaticUrl(byId.getStaticUrl());
        String redisKeyPrefix = "leadnews:article:hot:page:";
        String redisKeySuffix = redisKeyPrefix + "0";
        String channelKey = redisKeyPrefix + byId.getChannelId();
        String cacheStr = JSON.toJSONString(cache);
        Double score = redisTemplate.opsForZSet().score(redisKeySuffix, cacheStr);
//        文章不存在，根据数据库中的操作行为计算历史得分+本次操作得分，存入缓存
        if (score == null) {
            int historyScore = computeScore(byId);
            redisTemplate.opsForZSet().add(redisKeySuffix, cacheStr, historyScore + plusScore);
            redisTemplate.opsForZSet().add(channelKey, cacheStr, historyScore + plusScore);
        } else {
//        文章存在，更新追加本次增量得分
            redisTemplate.opsForZSet().incrementScore(redisKeySuffix, cacheStr, plusScore);
            redisTemplate.opsForZSet().incrementScore(channelKey, cacheStr, plusScore);
        }
//        数据库中的数据进行累加
        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApArticle::getId, byId.getId());
        updateWrapper.setSql("views = views +" + result.getView());
        updateWrapper.setSql("likes = likes +" + result.getLike());
        updateWrapper.setSql("comment = comment +" + result.getComment());
        updateWrapper.setSql("collection = collection +" + result.getCollect());
        articleService.update(updateWrapper);

    }

}
